package net.cyan.activex;

import java.lang.reflect.*;

import org.jawin.*;
import org.jawin.io.*;
import org.jawin.marshal.StructConverter;
import net.sf.cglib.core.CodeGenerationException;
import net.sf.cglib.proxy.*;

/**
 * <p>Title:dll调用代理 </p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2006</p>
 * <p>Company: </p>
 * @author ccs
 * @version 1.0
 */

public class DllProxy implements MethodInterceptor
{
   private String dll;

   private Class c;

   public DllProxy(String dll, Class c)
   {
      this.dll = dll;
      this.c = c;
   }

   public Object intercept(Object object, Method method, Object[] objectArray,
                           MethodProxy methodProxy) throws Throwable
   {
      if (Modifier.isAbstract(method.getModifiers()))
      {
         String methodName = method.getName();
         methodName = Character.toUpperCase(methodName.charAt(0)) +
             methodName.substring(1);

         FuncPtr func = null;
         try
         {
            func = new FuncPtr(dll, methodName);

            NakedByteStream nbs = new NakedByteStream();
            LittleEndianOutputStream leos = new LittleEndianOutputStream(nbs);
            String marshal = "";
            int l = 0;

            for (int i = 0; i < objectArray.length; i++)
            {
               Object obj = objectArray[i];
               if (obj instanceof Boolean)
               {
                  leos.writeInt( ( (Boolean) obj).booleanValue() ? 1 : 0);
                  l += 4;
                  marshal += "I";
               }
               else if (obj instanceof Double || obj instanceof Float)
               {
                  leos.writeDouble( ( (Number) obj).doubleValue());
                  l += 8;
                  marshal += "D";
               }
               else if (obj instanceof Number)
               {
                  leos.writeInt( ( (Number) obj).intValue());
                  l += 4;
                  marshal += "I";
               }
               else if (obj instanceof String)
               {
                  leos.writeStringUnicode( (String) obj);
                  l += 4;
                  marshal += "G";
               }
               else if (obj instanceof byte[])
               {
                  leos.write( (byte[]) obj);
                  l += 4;
                  marshal += "P" + ( (byte[]) obj).length;
               }
            }

            marshal += ":";
            Class c = method.getReturnType();
            if (c == Boolean.class || c == boolean.class)
            {
               marshal += "I";
            }
            else if (c == Double.class || c == Float.class || c == double.class ||
                     c == float.class)
            {
               marshal += "D";
            }
            else if (Number.class.isAssignableFrom(c) || (c.isPrimitive() && c != char.class))
            {
               marshal += "I";
            }
            else if (String.class == c)
            {
               marshal += "G";
            }

            marshal += ":";

            byte[] result = func.invoke(marshal, l, nbs, null,
                                        ReturnFlags.CHECK_NONE);

            if (c == Boolean.class || c == boolean.class)
            {
               return new Boolean(StructConverter.bytesIntoBoolean(result, 0));
            }
            else if (c == Double.class || c == double.class)
            {
               return new Double(StructConverter.bytesIntoDouble(result, 0));
            }
            else if (c == Float.class || c == float.class)
            {
               return new Float(StructConverter.bytesIntoFloat(result, 0));
            }
            else if (c == Byte.class || c == byte.class)
            {
               return new Byte(result[0]);
            }
            else if (c == Short.class || c == short.class)
            {
               return new Short(StructConverter.bytesIntoShort(result, 0));
            }
            else if (c == Integer.class || c == int.class)
            {
               return new Integer(StructConverter.bytesIntoInt(result, 0));
            }
            else if (c == Long.class || c == long.class)
            {
               return new Long(StructConverter.bytesIntoLong(result, 0));
            }
            else if (c == String.class)
            {
               return new String(result);
            }
            else if (c == byte[].class)
            {
               return result;
            }

            return null;
         }
         catch (Throwable ex)
         {
            throw new ActiveXException(c.getName() + "调用DLL方法" +
                                       methodName + "错误", ex);
         }
         finally
         {
            if (func != null)
            {
               try
               {
//                  func.close();
               }
               catch (Throwable ex)
               {
               }
            }
         }
      }
      else
      {
         return methodProxy.invokeSuper(object, objectArray);
      }
   }

   public static Dll createObject(Class c, String dll) throws
       ActiveXException
   {
      try
      {
         DllProxy proxy = new DllProxy(dll, c);
         return (Dll) Enhancer.create(c, proxy);
      }
      catch (CodeGenerationException ex)
      {
         Throwable cause = ex.getCause();
         if (cause instanceof RuntimeException)
         {
            throw (RuntimeException) cause;
         }
         else
         {
            throw new ActiveXException("创建DLL对象" + c.getName() + "失败:",
                                       cause);
         }
      }
   }
}
